<?php
/**
 * 首屏广告
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2019/6/3
 * @copyright PanxSoft Inc.
 */
namespace App\Http\Controllers\Admin\V1;


use App\Http\Controllers\ApiController;
use App\Http\Middleware\LocaleEnv;
use App\Http\Requests\Admin\V1\SplashAdvert\SplashAdvertDataRequest;
use App\Models\AdvertDescription;
use App\Models\AdvertType;
use App\Models\SplashAdvert;
use App\Models\Url;
use App\Services\AliYun\OSS;
use App\Services\Utils\TimeHelper;
use App\Services\Utils\Transaction;
use Illuminate\Http\Request;

class SplashAdvertController extends ApiController
{
    const ROUTES = [
        'index'   => 'admin.v1.SplashAdvert.index',
        'create'  => 'admin.v1.SplashAdvert.create',
        'store'   => 'admin.v1.SplashAdvert.store',
        'update'  => 'admin.v1.SplashAdvert.update',
        'destroy' => 'admin.v1.SplashAdvert.destroy',
    ];

    public function index()
    {
        $splash_adverts = (new SplashAdvert)
            ->oldest('sort_order')
            ->latest('created_at')
            ->with('advertDescriptions.redirectable')
            ->paginate(\request()->get('per_page') ?: 15);

        return $this->response(compact('splash_adverts'));
    }

    /**
     * @throws \Exception
     */
    public function create()
    {
        $oss_sign = (new OSS)->webUploaderSign((new SplashAdvert)->contentKeyPrefix());
        $advert_types = AdvertType::all();

        return $this->response(compact('oss_sign', 'advert_types'));
    }

    /**
     * @param SplashAdvertDataRequest $request
     *
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function store(SplashAdvertDataRequest $request)
    {
        $splash_advert =Transaction::handle(function () use ($request) {
            $splash_advert = (new SplashAdvert)->create($this->assembleAdvertStoreData($request));

            $this->storeAdvertDescriptions($splash_advert, $request->descriptions);

            return $splash_advert;
        });

        return $this->response(compact('splash_advert'));
    }

    /**
     * @param SplashAdvertDataRequest $request
     * @param $key
     *
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function update(SplashAdvertDataRequest $request, $key)
    {
        /** @var SplashAdvert $splash_advert */
        $splash_advert = (new SplashAdvert)->find($key);
            $this->checkItemExist($splash_advert, trans('checker.admin.splash_advert'));

        $updated = Transaction::handle(function () use ($splash_advert, $request) {

            $this->updateAdvertDescriptions($splash_advert, $request->descriptions);

            return $splash_advert->update($this->assembleAdvertStoreData($request));
        });

        return $this->response(compact('splash_advert', 'updated'));
    }

    /**
     * @param $key
     *
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function destroy($key)
    {
        $advert = (new SplashAdvert)->find($key);
            $this->checkItemExist($advert);

        $deleted = Transaction::handle(function () use ($advert) {
            // clean advert descriptions and redirect morph
            $advert->advertDescriptions
                ->load('redirectable')
                ->each(function (AdvertDescription $description) {
                    if ($description->redirectable) {
                        $description->redirectable->delete();
                    }

                    $description->delete();
                });

            $deleted = $advert->delete();

            return $deleted;
        });

        return $this->response(compact('deleted'));
    }

    /**
     * @param SplashAdvertDataRequest $request
     *
     * @return array
     */
    private function assembleAdvertStoreData(SplashAdvertDataRequest $request): array
    {
        $data = $request->only([
            'advert_type_id', 'play_second', 'name', 'can_skip',
            'sort_order', 'started_at', 'ended_at', 'content',
        ]);

        // admin panel request timezone convert to UTC time
        $data['started_at'] = TimeHelper::offsetMinutes($data['started_at'], LocaleEnv::timezoneOffset());
        $data['ended_at'] = TimeHelper::offsetMinutes($data['ended_at'], LocaleEnv::timezoneOffset());

        return $data;
    }

    /**
     * @param array $data
     *
     * @return array
     */
    private function convertRedirectUrlData(array $data): array
    {
        $type = $data['redirectable_type'] ?? null;

        if ($type === class_basename(Url::class)) {
            $data['redirectable_id'] = $this->createNewUrl($data['redirect_url'])->getKey();
        }

        return $data;
    }

    private function createNewUrl(string $url): Url
    {
        return (new Url)->create(compact('url'));
    }

    private function storeAdvertDescriptions(SplashAdvert $splash_advert, array $descriptions)
    {
        foreach ($descriptions as $language_id => $description) {
            $description = $this->convertRedirectUrlData($description);

            (new AdvertDescription)->create([
                'language_id'       => $language_id,
                'splash_advert_id'  => $splash_advert->getKey(),
                'content'           => $description['content'],
                'redirectable_id'   => $description['redirectable_id'] ?? 0,
                'redirectable_type' => $description['redirectable_type'] ?? '',
            ]);
        }
    }

    /**
     * @param SplashAdvert $splash_advert
     * @param array $descriptions
     */
    private function updateAdvertDescriptions(SplashAdvert $splash_advert, array $descriptions): void
    {
        $splash_advert->advertDescriptions
            ->load('redirectable')
            ->each(function (AdvertDescription $advertDescription) use ($descriptions) {
                if ($advertDescription->redirectable instanceof Url) {
                    $advertDescription->redirectable->delete();
                }

                if (! isset($descriptions[$advertDescription->language_id])) {
                    return;
                }

                $description = $this->convertRedirectUrlData($descriptions[$advertDescription->language_id]);

                $advertDescription->update([
                    'content'           => $description['content'],
                    'redirectable_id'   => $description['redirectable_id'] ?? 0,
                    'redirectable_type' => $description['redirectable_type'] ?? '',
                ]);
            });
    }
}